package com.hup.utils.commons.start;

import com.hup.utils.commons.TextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
 * 实现springboot启动进度反馈功能
 */
@Slf4j
@Component
public class StartUpProcessor implements BeanFactoryPostProcessor, BeanPostProcessor {

    private static ArrayList<StartUpProcessorHandler> HANDLERS = new ArrayList<>();

    public static void registerHandler(StartUpProcessorHandler handler) {
        HANDLERS.add(handler);
    }

    public static void removeHandler(StartUpProcessorHandler handler) {
        HANDLERS.remove(handler);
    }

    public static void startUpFinish() {
        //启动完后,本类已经无其他作用
        //HANDLERS置空后,需要将本类实例从监听中移除!否则会引起HANDLERS空指针异常
        DefaultListableBeanFactory beanFactory = SpringContext.getBeanFactory();

        /*
        注销:postProcessAfterInitialization;
            必须在注销postProcessBeanFactory之前!
            倒序后,会导致getBean异常,但getBeanPostProcessors中还是有本类实例的!
        不注销
            否则web项目,首次调用api时,会有告警[ "某些bean" is not eligible for getting processed by all BeanPostProcessors]
            如:AcceptHeaderLocaleResolver,FixedThemeResolver,DefaultRequestToViewNameTranslator,SessionFlashMapManager
         */
        //StartUpProcessor startUpProcessor = beanFactory.getBean(StartUpProcessor.class);
        //beanFactory.getBeanPostProcessors().remove(startUpProcessor);

        //注销:postProcessBeanFactory
        String startUpProcessorName = beanFactory.getBeanNamesForType(StartUpProcessor.class)[0];
        beanFactory.removeBeanDefinition(startUpProcessorName);

        HANDLERS.clear();
        HANDLERS = null;
    }

    /**
     * 实际启动时progress一般不会达到total,因为存在[Lazy加载,springboot内部bean]等情况
     */
    private int total;
    private int progress;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        total = beanFactory.getBeanDefinitionCount();

        if (log.isDebugEnabled()) {
            String[] names = beanFactory.getBeanDefinitionNames();
            log.debug("[{}]", TextUtil.getText(names, "\n"));
            //启动后,postProcessAfterInitialization的数量仍然小于(小很多)total+lazy
            int lazy = 0;
            for (String name : names) {
                BeanDefinition definition = beanFactory.getBeanDefinition(name);
                if (definition.isLazyInit()) {
                    log.info("lazy={}", name);
                    lazy++;
                }
            }
            log.info("有{}个组件,lazy={}", total, lazy);
        }

        for (StartUpProcessorHandler handler : HANDLERS) {
            handler.onGetBeanTotal(total);
        }
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (HANDLERS == null) return bean;

        progress++;
        log.debug("{}/{}, name={}", progress, total, beanName);
        for (StartUpProcessorHandler HANDLER : HANDLERS) {
            HANDLER.onBeanInited();
        }
        return bean;
    }

    public interface StartUpProcessorHandler {
        void onGetBeanTotal(int total);

        void onBeanInited();
    }

}
